#pragma once

#include <iostream>
#include <cassert>
#include <queue>
#include <memory>
#include <cstdlib>
#include <pthread.h>
#include <unistd.h>
#include <sys/prctl.h>

#include "Log.hpp"
#include "Lock.hpp"

using namespace std;

int gThreadNum = 5;

template <class T>
class ThreadPool
{
private:
    ThreadPool(int threadNum = gThreadNum) : threadNum_(threadNum), isStart_(false)
    {
        assert(threadNum_ > 0);
        pthread_mutex_init(&mutex_, nullptr);
        pthread_cond_init(&cond_, nullptr);
    }

    ThreadPool(const ThreadPool<T> &) = delete;
    void operator=(const ThreadPool<T> &) = delete;
public:
    static ThreadPool<T> *getInstance()
    {
        static Mutex mutex;
        if(nullptr == instance)
        {
            LockGuard lockguard(&mutex);
            if(nullptr == instance)
            {
                instance = new ThreadPool<T>();
            }
        }

        return instance;
    }

    static void *threadRoutine(void *args)
    {
        pthread_detach(pthread_self());
        ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);
        prctl(PR_SET_NAME, "follower");
        while(1)
        {
            tp->lockQueue();
            while(!tp->haveTask())
            {
                tp->waitForTask();
            }

            T t = tp->pop();
            tp->unlockQueue();

            int one, two;
            char oper;
            t.get(&one, &two, &oper);

            Log() << "新线程完成计算任务: " << one << oper << two << "=" << t.run() << "\n";
        }
    }

    void start()
    {
        assert(!isStart_);

        for(int i = 0; i < threadNum_; ++i)
        {
            pthread_t temp;
            pthread_create(&temp, nullptr, threadRoutine, this);
        }
        isStart_ = true;
    }

    void push(const T &in)
    {
        lockQueue();
        taskQueue_.push(in);
        choiceThreadForHandler();
        unlockQueue();
    }

    ~ThreadPool()
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&cond_);
    }
private:
    void lockQueue()
    {
        pthread_mutex_lock(&mutex_);
    }

    void unlockQueue()
    {
        pthread_mutex_unlock(&mutex_);
    }

    bool haveTask()
    {
        return !taskQueue_.empty();
    }

    void waitForTask()
    {
        pthread_cond_wait(&cond_, &mutex_);
    }

    void choiceThreadForHandler()
    {
        pthread_cond_signal(&cond_);
    }

    T pop()
    {
        T temp = taskQueue_.front();
        taskQueue_.pop();
        return temp;
    }
private:
    bool isStart_;
    int threadNum_;
    queue<T> taskQueue_;
    pthread_mutex_t mutex_;
    pthread_cond_t cond_;

    static ThreadPool<T> *instance;
};

template <class T>
ThreadPool<T> *ThreadPool<T>::instance = nullptr;